﻿using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using NUnit.Framework;
using RenewalProjects.Libraries.Common.Testing.Interfaces;
using RenewalProjects.Libraries.Wcf.EasyProxy.CallBehaviors;
using RenewalProjects.Libraries.Wcf.EasyProxy.ChannelAllocators;
using RenewalProjects.Libraries.Wcf.EasyProxy.ProxyFactories;
using RenewalProjects.Libraries.Wcf.EasyProxy.ProxyFactories.Configuration;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.TestBuilders;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.TestData;
using Rhino.Mocks;

namespace RenewalProjects.Libraries.Wcf.EasyProxy.Tests.Fixtures
{
	[TestFixture]
	public class EasyProxyFactoryFixture : IUsesRhinoMocks
	{
		#region Properties

		public MockRepository Mock { get; set; }

		#endregion

		#region Test Methods

		[SetUp]
		public void InitTest()
		{
			EasyProxyFactoryConfiguration<ITest> config = EasyProxyFactory.GetConfiguration<ITest>();
			config.ResetToDefaults();
		}

		[Test]
		public void EasyProxyFactory_creates_a_proxy_with_custom_callbehavior()
		{
			ChannelAllocatorBase<ITest> channelAllocator = CreateFakeChannelAllocator(true);

			Mock.ReplayAll();

			DefaultCallBehavior<ITest> callBehavior = new DefaultCallBehavior<ITest>(channelAllocator);
			ITest proxy = EasyProxyFactory.CreateProxy<ITest>(callBehavior);

			Assert.IsNotNull(proxy);
			Assert.AreEqual("Danny", proxy.GetName());
		}

		[Test(Description = "This tests the fact that the config settings are asked before proxy creation takes palce")]
		public void EasyProxyFactory_checks_default_configuration_for_ITest_and_creates_proxy()
		{
			EasyProxyFactoryConfiguration<ITest> configuration = EasyProxyFactory.GetConfiguration<ITest>();

			Assert.IsNotNull(configuration);
			Assert.IsNotNull(configuration.CallBehaviorFactory);
			Assert.IsNotNull(configuration.ChannelAllocatorFactory);
			Assert.IsNotNull(configuration.ChannelFactoryFactory);

			ITest proxy = EasyProxyFactory.CreateProxy<ITest>();
			Assert.IsNotNull(proxy);
		}

		[Test]
		public void EasyProxyFactory_changes_callbehavior_and_creates_proxy()
		{
			var callbehavior = CreateFakeCallbehavior();

			Mock.ReplayAll();

			EasyProxyFactory.GetConfiguration<ITest>().CallBehaviorFactory = (ca) => callbehavior;

			ITest proxy = EasyProxyFactory.CreateProxy<ITest>();
			string name = proxy.GetName();

			Assert.AreEqual("Danny", name);
		}

		[Test]
		public void EasyProxyFactory_changes_channelallocator_and_creates_proxy()
		{
			ChannelAllocatorBase<ITest> channelAllocator = CreateFakeChannelAllocator(true);

			Mock.ReplayAll();

			EasyProxyFactory.GetConfiguration<ITest>().ChannelAllocatorFactory = (cf, e) => channelAllocator;

			ITest proxy = EasyProxyFactory.CreateProxy<ITest>();
			string name = proxy.GetName();

			Assert.AreEqual("Danny", name);
		}

		[Test]
		public void EasyProxyFactory_changes_channelfactory_and_creates_proxy()
		{
			IChannelFactory<ITest> channelFactory = CreateFakeChannelFactory();

			Mock.ReplayAll();

			EasyProxyFactory.GetConfiguration<ITest>().ChannelFactoryFactory =
				() => new ChannelFactoryResult<ITest>(channelFactory, new ServiceMetadataEndpoint());

			ITest proxy = EasyProxyFactory.CreateProxy<ITest>();
			string name = proxy.GetName();

			Assert.AreEqual("Danny", name);
		}

		#endregion

		#region Test Helpers

		private CallBehaviorBase<ITest> CreateFakeCallbehavior()
		{
			ChannelAllocatorBase<ITest> channelAllocator = CreateFakeChannelAllocator();
			CallBehaviorBase<ITest> callbehavior = Mock.StrictMock<CallBehaviorBase<ITest>>(channelAllocator);
			callbehavior.Expect(x => x.InvokeOperation(null, null)).IgnoreArguments().Return("Danny");
			return callbehavior;
		}

		private IChannelFactory<ITest> CreateFakeChannelFactory()
		{
			ITest testChannel = Mock.PartialMock<TestChannel>();
			testChannel.Expect(x => x.GetName()).Return("Danny");

			IChannelFactory<ITest> channelFactory = Mock.StrictMock<IChannelFactory<ITest>>();
			channelFactory.Expect(x => x.CreateChannel(null, null)).IgnoreArguments().Return(testChannel);
			return channelFactory;
		}

		private ChannelAllocatorBase<ITest> CreateFakeChannelAllocator(bool expectGetNameCall = false)
		{
			ITest testChannel = Mock.PartialMock<TestChannel>();
			if(expectGetNameCall)
			{
				testChannel.Expect(x => x.GetName()).Return("Danny");
			}

			ChannelAllocatorBuilder<ITest> channelAllocatorBuilder =
					new ChannelAllocatorBuilder<ITest>(Mock, testChannel);
			if(expectGetNameCall)
			{
				channelAllocatorBuilder.ExpectAvailableChannelRetrieval();
				channelAllocatorBuilder.ChannelHolder(x => x.ExpectEndCall());
			}

			return channelAllocatorBuilder.Build();
		}

		#endregion
	}
}